<!doctype html>
<html lang="en">
 <head> 
  <meta charset="UTF-8"> 
  <meta name="description" content="燎火的个人博客"> 
  <meta name="keyword" content="燎火,qq799378666,软件开发,java,c#,js,html,html5,css"> 
  <title>采用 GLM 从代码层面理解 OpenGL 坐标系统</title> 
  <script src="/js/jquery-1.11.1.min.js"></script> 
  <script src="/js/bootstrap-3.3.5/js/bootstrap.js"></script> 
  <link href="/js/bootstrap-3.3.5/css/bootstrap.css" rel="stylesheet"> 
  <script src="/js/base.js"></script> 
  <script src="/js/nav.js"></script> 
  <script src="/js/jquery.toTop.min.js"></script> 
  <link href="/style/article.css" rel="stylesheet"> 
 </head> 
 <body> 
  <div id="main"> 
   <h3 id="title">采用 GLM 从代码层面理解 OpenGL 坐标系统</h3> 
   <div id="content">
    <p style="box-sizing: inherit; -webkit-tap-highlight-color: transparent; margin-bottom: 16px; color: rgb(61, 70, 77); font-family: &quot;Pingfang SC&quot;, STHeiti, &quot;Lantinghei SC&quot;, &quot;Open Sans&quot;, Arial, &quot;Hiragino Sans GB&quot;, &quot;Microsoft YaHei&quot;, &quot;WenQuanYi Micro Hei&quot;, SimSun, sans-serif; white-space: normal; background-color: rgb(248, 248, 248);">关于 OpengGL 坐标系统，我很喜欢<a href="https://learnopengl.com/#!Getting-started/Coordinate-Systems" style="box-sizing: inherit; -webkit-tap-highlight-color: transparent; border: none; outline: 0px; transition: color 0.3s; text-decoration: none; color: rgb(68, 102, 187);">这篇</a>博客，这篇博客有中文版本的。博客英文版讲诉更让人容易理解。接下来要写的这篇博客是以上篇博客为参考，进一步从代码的层面来理解 OpengGL 坐标系统。</p>
    <p style="box-sizing: inherit; -webkit-tap-highlight-color: transparent; margin-bottom: 16px; color: rgb(61, 70, 77); font-family: &quot;Pingfang SC&quot;, STHeiti, &quot;Lantinghei SC&quot;, &quot;Open Sans&quot;, Arial, &quot;Hiragino Sans GB&quot;, &quot;Microsoft YaHei&quot;, &quot;WenQuanYi Micro Hei&quot;, SimSun, sans-serif; white-space: normal; background-color: rgb(248, 248, 248);">下面博客中的代码都可以在&nbsp;<a href="http://git.oschina.net/iirecord/blogsnippet" style="box-sizing: inherit; -webkit-tap-highlight-color: transparent; border: none; outline: 0px; transition: color 0.3s; text-decoration: none; color: rgb(68, 102, 187);">blogsnippet</a>/opengl/lefthand-or-righthand 目录下获取。上篇参考博客中有下面的流程图。这里假设你已经知道了整个流程。图中我标出的红色 glm 函数分别表示，通常用&nbsp;<code style="box-sizing: inherit; -webkit-tap-highlight-color: transparent; border-radius: 3px; padding: 2px; font-size: 13px;">glm::lookAt</code>&nbsp;函数创建 view matrix 而透视投影矩阵则用 glm::perspective 创建。下面将会测试这两个函数。让我们开始吧。</p>
    <p style="box-sizing: inherit; -webkit-tap-highlight-color: transparent; margin-bottom: 16px; color: rgb(61, 70, 77); font-family: &quot;Pingfang SC&quot;, STHeiti, &quot;Lantinghei SC&quot;, &quot;Open Sans&quot;, Arial, &quot;Hiragino Sans GB&quot;, &quot;Microsoft YaHei&quot;, &quot;WenQuanYi Micro Hei&quot;, SimSun, sans-serif; white-space: normal; background-color: rgb(248, 248, 248);"><img src="https://static.oschina.net/uploads/img/201701/12221155_dlh4.png" alt="coodinate" title="coodinate"></p>
    <h1 style="box-sizing: inherit; -webkit-tap-highlight-color: transparent; font-size: 24px; font-weight: 500; color: rgb(61, 70, 77); font-family: &quot;Pingfang SC&quot;, STHeiti, &quot;Lantinghei SC&quot;, &quot;Open Sans&quot;, Arial, &quot;Hiragino Sans GB&quot;, &quot;Microsoft YaHei&quot;, &quot;WenQuanYi Micro Hei&quot;, SimSun, sans-serif; white-space: normal; background-color: rgb(248, 248, 248);">NDC 是基于左手坐标系</h1>
    <p style="box-sizing: inherit; -webkit-tap-highlight-color: transparent; margin-bottom: 16px; color: rgb(61, 70, 77); font-family: &quot;Pingfang SC&quot;, STHeiti, &quot;Lantinghei SC&quot;, &quot;Open Sans&quot;, Arial, &quot;Hiragino Sans GB&quot;, &quot;Microsoft YaHei&quot;, &quot;WenQuanYi Micro Hei&quot;, SimSun, sans-serif; white-space: normal; background-color: rgb(248, 248, 248);">首先要知道 OpenGL NDC 是基于左手坐标系的，上篇推荐的博客中指出 NDC 是基于左手坐标系的。</p>
    <blockquote style="box-sizing: inherit; -webkit-tap-highlight-color: transparent; position: relative; font-size: 1.5rem; background: rgb(244, 244, 244); margin: 20px 0px; padding: 16px 24px 16px 35px; font-family: &quot;Pingfang SC&quot;, STHeiti, &quot;Lantinghei SC&quot;, &quot;Open Sans&quot;, Arial, &quot;Hiragino Sans GB&quot;, &quot;Microsoft YaHei&quot;, &quot;WenQuanYi Micro Hei&quot;, SimSun, sans-serif; white-space: normal;">
     <p style="box-sizing: inherit; -webkit-tap-highlight-color: transparent; margin-bottom: 16px;">Note that in normalized device coordinates OpenGL actually uses a left-handed system (the projection matrix switches the handedness).</p>
    </blockquote>
    <p style="box-sizing: inherit; -webkit-tap-highlight-color: transparent; margin-bottom: 16px; color: rgb(61, 70, 77); font-family: &quot;Pingfang SC&quot;, STHeiti, &quot;Lantinghei SC&quot;, &quot;Open Sans&quot;, Arial, &quot;Hiragino Sans GB&quot;, &quot;Microsoft YaHei&quot;, &quot;WenQuanYi Micro Hei&quot;, SimSun, sans-serif; white-space: normal; background-color: rgb(248, 248, 248);">继续往下看，你将会发现它的用处。下面就写程序来验证一下。先程序运行结果。</p>
    <p style="box-sizing: inherit; -webkit-tap-highlight-color: transparent; margin-bottom: 16px; color: rgb(61, 70, 77); font-family: &quot;Pingfang SC&quot;, STHeiti, &quot;Lantinghei SC&quot;, &quot;Open Sans&quot;, Arial, &quot;Hiragino Sans GB&quot;, &quot;Microsoft YaHei&quot;, &quot;WenQuanYi Micro Hei&quot;, SimSun, sans-serif; white-space: normal; background-color: rgb(248, 248, 248);"><img src="https://static.oschina.net/uploads/img/201701/11204740_c7rG.png" alt="ndc" title="ndc"></p>
    <p style="box-sizing: inherit; -webkit-tap-highlight-color: transparent; margin-bottom: 16px; color: rgb(61, 70, 77); font-family: &quot;Pingfang SC&quot;, STHeiti, &quot;Lantinghei SC&quot;, &quot;Open Sans&quot;, Arial, &quot;Hiragino Sans GB&quot;, &quot;Microsoft YaHei&quot;, &quot;WenQuanYi Micro Hei&quot;, SimSun, sans-serif; white-space: normal; background-color: rgb(248, 248, 248);">程序中一会绘制了 5 个图形，左半部分两个，右半部份两个，中间的矩形一个。点坐标如下</p>
    <pre class="hljs go" style="box-sizing: inherit; -webkit-tap-highlight-color: transparent; overflow-x: auto; padding: 2px; background-color: rgb(63, 63, 63); color: rgb(220, 220, 220); border-radius: 3px; line-height: 1.4; word-wrap: normal; font-size: 13px; font-family: Menlo, Monaco, Consolas, &quot;Courier New&quot;, monospace;">const&nbsp;GLfloat&nbsp;vertices_leftup[]&nbsp;=&nbsp;{&nbsp;//&nbsp;left&nbsp;up&nbsp;red
&nbsp;&nbsp;&nbsp;&nbsp;-1.0f,&nbsp;-0.5f,&nbsp;-1.0f,&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;0.0f,&nbsp;-0.5f,&nbsp;-1.0f,&nbsp;&nbsp;&nbsp;&nbsp;-0.5f,&nbsp;&nbsp;1.0f,&nbsp;-1.0f,
};const&nbsp;GLfloat&nbsp;vertices_leftdown[]&nbsp;=&nbsp;{&nbsp;//&nbsp;left&nbsp;down&nbsp;blue
&nbsp;&nbsp;&nbsp;&nbsp;-1.0f,&nbsp;-1.0f,&nbsp;0.5f,&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;0.0f,&nbsp;-1.0f,&nbsp;0.5f,&nbsp;&nbsp;&nbsp;&nbsp;-0.5f,&nbsp;&nbsp;0.5f,&nbsp;0.5f,
};const&nbsp;GLfloat&nbsp;vertices_rightup[]&nbsp;=&nbsp;{&nbsp;//&nbsp;right&nbsp;up&nbsp;red
&nbsp;&nbsp;&nbsp;&nbsp;0.0f,&nbsp;-0.5f,&nbsp;0.5f,&nbsp;&nbsp;&nbsp;&nbsp;1.0f,&nbsp;-0.5f,&nbsp;0.5f,&nbsp;&nbsp;&nbsp;&nbsp;0.5f,&nbsp;&nbsp;1.0f,&nbsp;0.5f,
};const&nbsp;GLfloat&nbsp;vertices_rightdown[]&nbsp;=&nbsp;{&nbsp;//&nbsp;right&nbsp;down&nbsp;blue
&nbsp;&nbsp;&nbsp;&nbsp;0.0f,&nbsp;-1.0f,&nbsp;-1.0f,&nbsp;&nbsp;&nbsp;&nbsp;1.0f,&nbsp;-1.0f,&nbsp;-1.0f,&nbsp;&nbsp;&nbsp;&nbsp;0.5f,&nbsp;&nbsp;0.5f,&nbsp;-1.0f,
};const&nbsp;GLfloat&nbsp;vertices_rect[]&nbsp;=&nbsp;{&nbsp;//&nbsp;center&nbsp;green
&nbsp;&nbsp;&nbsp;&nbsp;-1.0f,&nbsp;-0.5f,&nbsp;0.0f,&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;1.0f,&nbsp;-0.5f,&nbsp;0.0f,&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;1.0f,&nbsp;&nbsp;0.5f,&nbsp;0.0f,&nbsp;&nbsp;&nbsp;&nbsp;-1.0f,&nbsp;&nbsp;0.5f,&nbsp;0.0f,
};</pre>
    <p style="box-sizing: inherit; -webkit-tap-highlight-color: transparent; margin-bottom: 16px; color: rgb(61, 70, 77); font-family: &quot;Pingfang SC&quot;, STHeiti, &quot;Lantinghei SC&quot;, &quot;Open Sans&quot;, Arial, &quot;Hiragino Sans GB&quot;, &quot;Microsoft YaHei&quot;, &quot;WenQuanYi Micro Hei&quot;, SimSun, sans-serif; white-space: normal; background-color: rgb(248, 248, 248);">顶点的坐标本身就处于 [-1, 1] 范围内，我们也未做任何坐标变换，因此这些坐标就等于最终的 NDC 坐标值。若 NDC 是基于左手坐标系，则 z 轴上的坐标值越小，就越显示在前面。按照 z 值从小到大排序则是&nbsp;<code style="box-sizing: inherit; -webkit-tap-highlight-color: transparent; border-radius: 3px; padding: 2px; font-size: 13px;">left up red, right down blue(-1.0f) &gt; center green(0.0f) &gt; left down blue, right up red(0.5f)</code>&nbsp;。左上角的红色三角形与右下角的蓝色三角形显示在最前面，中间的绿色矩形显示在中间，左下的蓝色三角形和右上的红色三角形显示在最远处。验证了 NDC 是基于左手坐标系。注意，代码中要开启深度测试。</p>
    <h1 style="box-sizing: inherit; -webkit-tap-highlight-color: transparent; font-size: 24px; font-weight: 500; color: rgb(61, 70, 77); font-family: &quot;Pingfang SC&quot;, STHeiti, &quot;Lantinghei SC&quot;, &quot;Open Sans&quot;, Arial, &quot;Hiragino Sans GB&quot;, &quot;Microsoft YaHei&quot;, &quot;WenQuanYi Micro Hei&quot;, SimSun, sans-serif; white-space: normal; background-color: rgb(248, 248, 248);">lookAt 参数</h1>
    <pre class="hljs cpp" style="box-sizing: inherit; -webkit-tap-highlight-color: transparent; overflow-x: auto; padding: 2px; background-color: rgb(63, 63, 63); color: rgb(220, 220, 220); border-radius: 3px; line-height: 1.4; word-wrap: normal; font-size: 13px; font-family: Menlo, Monaco, Consolas, &quot;Courier New&quot;, monospace;">///&nbsp;Build&nbsp;a&nbsp;look&nbsp;at&nbsp;view&nbsp;matrix&nbsp;based&nbsp;on&nbsp;the&nbsp;default&nbsp;handedness.///&nbsp;@param&nbsp;eye&nbsp;Position&nbsp;of&nbsp;the&nbsp;camera///&nbsp;@param&nbsp;center&nbsp;Position&nbsp;where&nbsp;the&nbsp;camera&nbsp;is&nbsp;looking&nbsp;at///&nbsp;@param&nbsp;up&nbsp;Normalized&nbsp;up&nbsp;vector,&nbsp;how&nbsp;the&nbsp;camera&nbsp;is&nbsp;oriented.&nbsp;Typically&nbsp;(0,&nbsp;0,&nbsp;1)template&nbsp;&lt;typename&nbsp;T,&nbsp;precision&nbsp;P&gt;
GLM_FUNC_DECL&nbsp;tmat4x4&lt;T,&nbsp;P&gt;&nbsp;lookAt(
&nbsp;&nbsp;&nbsp;&nbsp;tvec3&lt;T,&nbsp;P&gt;&nbsp;const&nbsp;&amp;&nbsp;eye,
&nbsp;&nbsp;&nbsp;&nbsp;tvec3&lt;T,&nbsp;P&gt;&nbsp;const&nbsp;&amp;&nbsp;center,
&nbsp;&nbsp;&nbsp;&nbsp;tvec3&lt;T,&nbsp;P&gt;&nbsp;const&nbsp;&amp;&nbsp;up);</pre>
    <p style="box-sizing: inherit; -webkit-tap-highlight-color: transparent; margin-bottom: 16px; color: rgb(61, 70, 77); font-family: &quot;Pingfang SC&quot;, STHeiti, &quot;Lantinghei SC&quot;, &quot;Open Sans&quot;, Arial, &quot;Hiragino Sans GB&quot;, &quot;Microsoft YaHei&quot;, &quot;WenQuanYi Micro Hei&quot;, SimSun, sans-serif; white-space: normal; background-color: rgb(248, 248, 248);"><code style="box-sizing: inherit; -webkit-tap-highlight-color: transparent; border-radius: 3px; padding: 2px; font-size: 13px;">glm::lookAt</code>&nbsp;可用于产生视图矩阵（view matrix）将 world space 中的点转换到 view/eye space 。函数参数 eye 是 world space 中摄像机的坐标位置，参数 center 是 world space 中摄像机指向的点，up 是指向上方的向量，通常是 (0, 0, 1) 。lookAt 的结果受到是左手坐标系还是右手坐标系的影响，若代码中定义了宏&nbsp;<code style="box-sizing: inherit; -webkit-tap-highlight-color: transparent; border-radius: 3px; padding: 2px; font-size: 13px;">GLM_LEFT_HANDED</code>&nbsp;则 glm 调用左手坐标系版本，若没有定义则调用右手坐标系版本，默认是没有定义的，因此默认用的就是右手坐标系版本，<code style="box-sizing: inherit; -webkit-tap-highlight-color: transparent; border-radius: 3px; padding: 2px; font-size: 13px;">glm::perspective</code>&nbsp;也一样受到此宏的影响。</p>
    <p style="box-sizing: inherit; -webkit-tap-highlight-color: transparent; margin-bottom: 16px; color: rgb(61, 70, 77); font-family: &quot;Pingfang SC&quot;, STHeiti, &quot;Lantinghei SC&quot;, &quot;Open Sans&quot;, Arial, &quot;Hiragino Sans GB&quot;, &quot;Microsoft YaHei&quot;, &quot;WenQuanYi Micro Hei&quot;, SimSun, sans-serif; white-space: normal; background-color: rgb(248, 248, 248);">下面就通过代码来验证&nbsp;<code style="box-sizing: inherit; -webkit-tap-highlight-color: transparent; border-radius: 3px; padding: 2px; font-size: 13px;">glm::lookAt</code>函数参数中点的位置是 world space 。需求就是计算 world space 某一点&nbsp;<code style="box-sizing: inherit; -webkit-tap-highlight-color: transparent; border-radius: 3px; padding: 2px; font-size: 13px;">somepoint</code>&nbsp;到摄像机的距离。有两种计算方式。</p>
    <ul style="box-sizing: inherit; -webkit-tap-highlight-color: transparent; margin-left: 30px; padding: 8px 15px; color: rgb(61, 70, 77); font-family: &quot;Pingfang SC&quot;, STHeiti, &quot;Lantinghei SC&quot;, &quot;Open Sans&quot;, Arial, &quot;Hiragino Sans GB&quot;, &quot;Microsoft YaHei&quot;, &quot;WenQuanYi Micro Hei&quot;, SimSun, sans-serif; white-space: normal; background-color: rgb(248, 248, 248);" class=" list-paddingleft-2">
     <li><p>在 world space 中计算点与摄像机之间的距离。</p></li>
     <li><p>在 view/eye space 中计算点与摄像机之间的距离。</p></li>
    </ul>
    <p style="box-sizing: inherit; -webkit-tap-highlight-color: transparent; margin-bottom: 16px; color: rgb(61, 70, 77); font-family: &quot;Pingfang SC&quot;, STHeiti, &quot;Lantinghei SC&quot;, &quot;Open Sans&quot;, Arial, &quot;Hiragino Sans GB&quot;, &quot;Microsoft YaHei&quot;, &quot;WenQuanYi Micro Hei&quot;, SimSun, sans-serif; white-space: normal; background-color: rgb(248, 248, 248);">具体代码如下。查看输出结果二者计算的距离是一致的，验证了我们的想法。<strong style="box-sizing: inherit; -webkit-tap-highlight-color: transparent;">代码中通过计算 view matrix 的逆矩阵，可以得到摄像机的位置坐标，还蛮有用处的</strong>。</p>
    <pre class="hljs cpp" style="box-sizing: inherit; -webkit-tap-highlight-color: transparent; overflow-x: auto; padding: 2px; background-color: rgb(63, 63, 63); color: rgb(220, 220, 220); border-radius: 3px; line-height: 1.4; word-wrap: normal; font-size: 13px; font-family: Menlo, Monaco, Consolas, &quot;Courier New&quot;, monospace;">voidcalc_distance_from_camera()&nbsp;{
&nbsp;&nbsp;&nbsp;&nbsp;glm::vec4&nbsp;somepoint(5.0f,&nbsp;5.0f,&nbsp;5.0f,&nbsp;1.0f);
&nbsp;&nbsp;&nbsp;&nbsp;glm::vec3&nbsp;camerapos(2.0f,&nbsp;2.0f,&nbsp;2.0f);
&nbsp;&nbsp;&nbsp;&nbsp;glm::mat4&nbsp;viewmat&nbsp;=&nbsp;glm::lookAt(camerapos,&nbsp;glm::vec3(0,&nbsp;0,&nbsp;0),&nbsp;glm::vec3(0,&nbsp;1.0f,&nbsp;0));
&nbsp;&nbsp;&nbsp;&nbsp;glm::vec4&nbsp;somepoint_view&nbsp;=&nbsp;viewmat&nbsp;*&nbsp;somepoint;&nbsp;//&nbsp;transform&nbsp;somepoint&nbsp;to&nbsp;view&nbsp;space

&nbsp;&nbsp;&nbsp;&nbsp;//&nbsp;当只有&nbsp;view&nbsp;matrix&nbsp;时，也可以计算出摄像机的位置，下面代码就展示了这种计算方式，
&nbsp;&nbsp;&nbsp;&nbsp;//&nbsp;至于原理可以先忽略，涉及到数学的部分总是让人害怕。
&nbsp;&nbsp;&nbsp;&nbsp;glm::mat4&nbsp;viewmat_inverse&nbsp;=&nbsp;glm::inverse(viewmat);
&nbsp;&nbsp;&nbsp;&nbsp;glm::vec3&nbsp;camerapos_calc(viewmat_inverse[3]);&nbsp;&nbsp;&nbsp;&nbsp;printf("here&nbsp;camera&nbsp;pos:&nbsp;%.2f&nbsp;%.2f,&nbsp;%.2f\n",&nbsp;camerapos.x,&nbsp;camerapos.y,&nbsp;camerapos.z);&nbsp;&nbsp;&nbsp;&nbsp;printf("calc&nbsp;camera&nbsp;pos:&nbsp;%.2f&nbsp;%.2f,&nbsp;%.2f\n",&nbsp;camerapos_calc.x,&nbsp;camerapos_calc.y,&nbsp;camerapos_calc.z);&nbsp;&nbsp;&nbsp;&nbsp;//&nbsp;计算点&nbsp;somepoint&nbsp;距离摄像机的距离
&nbsp;&nbsp;&nbsp;&nbsp;//&nbsp;方式&nbsp;1&nbsp;，在&nbsp;world&nbsp;space&nbsp;中计算距离
&nbsp;&nbsp;&nbsp;&nbsp;float&nbsp;distance_world&nbsp;=&nbsp;glm::distance2(glm::vec3(somepoint),&nbsp;camerapos_calc);&nbsp;&nbsp;&nbsp;&nbsp;printf("distance&nbsp;calc&nbsp;in&nbsp;world&nbsp;space:%.2f\n",&nbsp;distance_world);&nbsp;&nbsp;&nbsp;&nbsp;//&nbsp;方式&nbsp;2&nbsp;，在&nbsp;view&nbsp;space&nbsp;中计算距离，
&nbsp;&nbsp;&nbsp;&nbsp;//&nbsp;view&nbsp;space&nbsp;就是在摄像机位置中观看对象，此时摄像机就相当于原点
&nbsp;&nbsp;&nbsp;&nbsp;glm::vec3&nbsp;camerapos_view(0,&nbsp;0,&nbsp;0);&nbsp;&nbsp;&nbsp;&nbsp;float&nbsp;distance_view&nbsp;=&nbsp;glm::distance2(glm::vec3(somepoint_view),&nbsp;camerapos_view);&nbsp;&nbsp;&nbsp;&nbsp;printf("distance&nbsp;calc&nbsp;in&nbsp;view&nbsp;space:%.2f\n",&nbsp;distance_view);
}

输出结果如下：
here&nbsp;camera&nbsp;pos:&nbsp;2.00&nbsp;2.00,&nbsp;2.00calc&nbsp;camera&nbsp;pos:&nbsp;2.00&nbsp;2.00,&nbsp;2.00distance&nbsp;calc&nbsp;in&nbsp;world&nbsp;space:27.00distance&nbsp;calc&nbsp;in&nbsp;view&nbsp;space:27.00`</pre>
    <h1 style="box-sizing: inherit; -webkit-tap-highlight-color: transparent; font-size: 24px; font-weight: 500; color: rgb(61, 70, 77); font-family: &quot;Pingfang SC&quot;, STHeiti, &quot;Lantinghei SC&quot;, &quot;Open Sans&quot;, Arial, &quot;Hiragino Sans GB&quot;, &quot;Microsoft YaHei&quot;, &quot;WenQuanYi Micro Hei&quot;, SimSun, sans-serif; white-space: normal; background-color: rgb(248, 248, 248);">perspective 参数</h1>
    <pre class="hljs cpp" style="box-sizing: inherit; -webkit-tap-highlight-color: transparent; overflow-x: auto; padding: 2px; background-color: rgb(63, 63, 63); color: rgb(220, 220, 220); border-radius: 3px; line-height: 1.4; word-wrap: normal; font-size: 13px; font-family: Menlo, Monaco, Consolas, &quot;Courier New&quot;, monospace;">///&nbsp;Creates&nbsp;a&nbsp;matrix&nbsp;for&nbsp;a&nbsp;symetric&nbsp;perspective-view&nbsp;frustum&nbsp;based&nbsp;on&nbsp;the&nbsp;default&nbsp;handedness.///&nbsp;@param&nbsp;fovy&nbsp;Specifies&nbsp;the&nbsp;field&nbsp;of&nbsp;view&nbsp;angle&nbsp;in&nbsp;the&nbsp;y&nbsp;direction.&nbsp;Expressed&nbsp;in&nbsp;radians.///&nbsp;@param&nbsp;aspect&nbsp;Specifies&nbsp;the&nbsp;aspect&nbsp;ratio&nbsp;that&nbsp;determines&nbsp;the&nbsp;field&nbsp;of&nbsp;view&nbsp;in&nbsp;the&nbsp;x&nbsp;direction.&nbsp;The&nbsp;aspect&nbsp;ratio&nbsp;is&nbsp;the&nbsp;ratio&nbsp;of&nbsp;x&nbsp;(width)&nbsp;to&nbsp;y&nbsp;(height).///&nbsp;@param&nbsp;near&nbsp;Specifies&nbsp;the&nbsp;distance&nbsp;from&nbsp;the&nbsp;viewer&nbsp;to&nbsp;the&nbsp;near&nbsp;clipping&nbsp;plane&nbsp;(always&nbsp;positive).///&nbsp;@param&nbsp;far&nbsp;Specifies&nbsp;the&nbsp;distance&nbsp;from&nbsp;the&nbsp;viewer&nbsp;to&nbsp;the&nbsp;far&nbsp;clipping&nbsp;plane&nbsp;(always&nbsp;positive).///&nbsp;@tparam&nbsp;T&nbsp;Value&nbsp;type&nbsp;used&nbsp;to&nbsp;build&nbsp;the&nbsp;matrix.&nbsp;Currently&nbsp;supported:&nbsp;half&nbsp;(not&nbsp;recommanded),&nbsp;float&nbsp;or&nbsp;double.template&nbsp;&lt;typename&nbsp;T&gt;
GLM_FUNC_DECL&nbsp;tmat4x4&lt;T,&nbsp;defaultp&gt;&nbsp;perspective(T&nbsp;fovy,&nbsp;T&nbsp;aspect,&nbsp;T&nbsp;near,&nbsp;T&nbsp;far);</pre>
    <p style="box-sizing: inherit; -webkit-tap-highlight-color: transparent; margin-bottom: 16px; color: rgb(61, 70, 77); font-family: &quot;Pingfang SC&quot;, STHeiti, &quot;Lantinghei SC&quot;, &quot;Open Sans&quot;, Arial, &quot;Hiragino Sans GB&quot;, &quot;Microsoft YaHei&quot;, &quot;WenQuanYi Micro Hei&quot;, SimSun, sans-serif; white-space: normal; background-color: rgb(248, 248, 248);"><code style="box-sizing: inherit; -webkit-tap-highlight-color: transparent; border-radius: 3px; padding: 2px; font-size: 13px;">glm::perspective</code>&nbsp;用于产生 3D 透视投影矩阵。实际上这个矩阵定义了一个 frustum ，位于 frustum 中的点不会被 clip 。frustum 如下图。</p>
    <p style="box-sizing: inherit; -webkit-tap-highlight-color: transparent; margin-bottom: 16px; color: rgb(61, 70, 77); font-family: &quot;Pingfang SC&quot;, STHeiti, &quot;Lantinghei SC&quot;, &quot;Open Sans&quot;, Arial, &quot;Hiragino Sans GB&quot;, &quot;Microsoft YaHei&quot;, &quot;WenQuanYi Micro Hei&quot;, SimSun, sans-serif; white-space: normal; background-color: rgb(248, 248, 248);"><img src="https://static.oschina.net/uploads/img/201701/11230628_OKfe.png" alt="frustum" title="frustum"></p>
    <p style="box-sizing: inherit; -webkit-tap-highlight-color: transparent; margin-bottom: 16px; color: rgb(61, 70, 77); font-family: &quot;Pingfang SC&quot;, STHeiti, &quot;Lantinghei SC&quot;, &quot;Open Sans&quot;, Arial, &quot;Hiragino Sans GB&quot;, &quot;Microsoft YaHei&quot;, &quot;WenQuanYi Micro Hei&quot;, SimSun, sans-serif; white-space: normal; background-color: rgb(248, 248, 248);">参数 fovy 表示 field of view ，aspect 表示屏幕宽高比，这两个参数都很好理解。 后面的两个参数 near 和 far 定义了 frustum 的近平面和远平面的距离。<strong style="box-sizing: inherit; -webkit-tap-highlight-color: transparent;">这是以世界坐标系（world space）为参照坐标系，因此 near 和 far 其实定义的是距离世界坐标系原点 (0, 0, 0) 的距离，从远平面的 4 个点到近平面的相应的 4 个点所在的 4 条直线必然相较于坐标系原点，如上图中的原点位置</strong>。我之前就把这里的原点与 view space 中以摄像机为原点搞混淆了，其实在准备透视投影时，就是在 world space 中处理坐标点。</p>
    <p style="box-sizing: inherit; -webkit-tap-highlight-color: transparent; margin-bottom: 16px; color: rgb(61, 70, 77); font-family: &quot;Pingfang SC&quot;, STHeiti, &quot;Lantinghei SC&quot;, &quot;Open Sans&quot;, Arial, &quot;Hiragino Sans GB&quot;, &quot;Microsoft YaHei&quot;, &quot;WenQuanYi Micro Hei&quot;, SimSun, sans-serif; white-space: normal; background-color: rgb(248, 248, 248);">上图发现没，没有指定 z 轴的方向。因为涉及到具体的实现时，z 轴的方向是受左手坐标系还是右手坐标系影响，<code style="box-sizing: inherit; -webkit-tap-highlight-color: transparent; border-radius: 3px; padding: 2px; font-size: 13px;">glm::perspective</code>&nbsp;同样也是受到宏&nbsp;<code style="box-sizing: inherit; -webkit-tap-highlight-color: transparent; border-radius: 3px; padding: 2px; font-size: 13px;">GLM_LEFT_HANDED</code>&nbsp;的控制来决定调用左手坐标系实现还是右手坐标系实现。先不管左手还是右手坐标系，有一点需要记住就是<strong style="box-sizing: inherit; -webkit-tap-highlight-color: transparent;">透视投影时，同一个物体，越靠近近平面就显示越靠前也同时比较大，越靠近远平面就显示越靠后同时也较小</strong>。这样<strong style="box-sizing: inherit; -webkit-tap-highlight-color: transparent;">当在右手坐标系时，z 轴的值越大，则物体越近。当在左手坐标系时，z 轴的值越小，则物体越近</strong>。当进行 3D 透视投影时，在 view matrix 转换物体坐标后，落在透视投影矩阵定义的坐标范围外的点就会被剪裁（clip）。</p>
    <p style="box-sizing: inherit; -webkit-tap-highlight-color: transparent; margin-bottom: 16px; color: rgb(61, 70, 77); font-family: &quot;Pingfang SC&quot;, STHeiti, &quot;Lantinghei SC&quot;, &quot;Open Sans&quot;, Arial, &quot;Hiragino Sans GB&quot;, &quot;Microsoft YaHei&quot;, &quot;WenQuanYi Micro Hei&quot;, SimSun, sans-serif; white-space: normal; background-color: rgb(248, 248, 248);">上面提到的左手坐标系和右手坐标系对于开发者来说，就可以根据项目需求来决定物体的坐标是采用右手坐标系指定，还是左手坐标系指定。一般在 OpenGL 的实际项目中大都习惯采用右手坐标系来指定物体的坐标。</p>
    <p style="box-sizing: inherit; -webkit-tap-highlight-color: transparent; margin-bottom: 16px; color: rgb(61, 70, 77); font-family: &quot;Pingfang SC&quot;, STHeiti, &quot;Lantinghei SC&quot;, &quot;Open Sans&quot;, Arial, &quot;Hiragino Sans GB&quot;, &quot;Microsoft YaHei&quot;, &quot;WenQuanYi Micro Hei&quot;, SimSun, sans-serif; white-space: normal; background-color: rgb(248, 248, 248);">举个具体的例子，当仅调用&nbsp;<code style="box-sizing: inherit; -webkit-tap-highlight-color: transparent; border-radius: 3px; padding: 2px; font-size: 13px;">glm::perspective(glm::radians(45.0f), 800.0f/600.0f, 1.0f, 100.0f);</code>&nbsp;产生透视投影矩阵进行坐标变换。</p>
    <ul style="box-sizing: inherit; -webkit-tap-highlight-color: transparent; margin-left: 30px; padding: 8px 15px; color: rgb(61, 70, 77); font-family: &quot;Pingfang SC&quot;, STHeiti, &quot;Lantinghei SC&quot;, &quot;Open Sans&quot;, Arial, &quot;Hiragino Sans GB&quot;, &quot;Microsoft YaHei&quot;, &quot;WenQuanYi Micro Hei&quot;, SimSun, sans-serif; white-space: normal; background-color: rgb(248, 248, 248);" class=" list-paddingleft-2">
     <li><p>当采用右手坐标系时，z 轴坐标值范围在 [-1.0f, -100.0f] 。-1.0f 是最近的 z 轴坐标，-100.0f 是最远的 z 轴坐标。</p></li>
     <li><p>当采用左手坐标系时，z 轴坐标值范围在 [1.0f, 100.0f] 。1.0f 是最近的 z 轴坐标，100.0f 是最远的 z 轴坐标。</p></li>
    </ul>
    <p style="box-sizing: inherit; -webkit-tap-highlight-color: transparent; margin-bottom: 16px; color: rgb(61, 70, 77); font-family: &quot;Pingfang SC&quot;, STHeiti, &quot;Lantinghei SC&quot;, &quot;Open Sans&quot;, Arial, &quot;Hiragino Sans GB&quot;, &quot;Microsoft YaHei&quot;, &quot;WenQuanYi Micro Hei&quot;, SimSun, sans-serif; white-space: normal; background-color: rgb(248, 248, 248);">下面写一段代码来验证上面这个例子。取近平面对应于 NDC 的一个点 (0, 0, -1.0f, 1.0f) 和 远平面对应于 NDC 的一个点 (0, 0, 1.0f, 1.0f) ，逆运算他们对应的 world space 中的点，我们采用右手坐标系，并查看最终的点的 z 轴上的值是不是分别是 -1.0f, -100.0f 。记得前面说的 NDC 是基于左手坐标系，所以对上面取的两个 NDC 坐标不会有疑惑吧。</p>
    <pre class="hljs cpp" style="box-sizing: inherit; -webkit-tap-highlight-color: transparent; overflow-x: auto; padding: 2px; background-color: rgb(63, 63, 63); color: rgb(220, 220, 220); border-radius: 3px; line-height: 1.4; word-wrap: normal; font-size: 13px; font-family: Menlo, Monaco, Consolas, &quot;Courier New&quot;, monospace;">void&nbsp;calc_near_far()&nbsp;{&nbsp;&nbsp;&nbsp;&nbsp;//&nbsp;采用右手坐标系验证
&nbsp;&nbsp;&nbsp;&nbsp;float&nbsp;neardistance&nbsp;=&nbsp;1.0f;&nbsp;&nbsp;&nbsp;&nbsp;float&nbsp;fardistance&nbsp;=&nbsp;100.0f;
&nbsp;&nbsp;&nbsp;&nbsp;glm::mat4&nbsp;persmat&nbsp;=&nbsp;glm::perspectiveRH(glm::radians(45.0f),&nbsp;800.0f/600.0f,&nbsp;neardistance,&nbsp;fardistance);&nbsp;&nbsp;&nbsp;&nbsp;//&nbsp;NDC&nbsp;是基于左手坐标系的，近平面对应的&nbsp;NDC&nbsp;坐标的&nbsp;z&nbsp;轴的值是&nbsp;-1.0f&nbsp;，
&nbsp;&nbsp;&nbsp;&nbsp;//&nbsp;而远平面对应的&nbsp;NDC&nbsp;坐标的&nbsp;z&nbsp;轴的值是&nbsp;1.0f&nbsp;。
&nbsp;&nbsp;&nbsp;&nbsp;glm::vec4&nbsp;near_ndc(0,&nbsp;0,&nbsp;-1.0f,&nbsp;1.0f);
&nbsp;&nbsp;&nbsp;&nbsp;glm::vec4&nbsp;far_ndc(0,&nbsp;0,&nbsp;1.0f,&nbsp;1.0f);&nbsp;&nbsp;&nbsp;&nbsp;//&nbsp;由于我们逆运算这个&nbsp;ndc&nbsp;坐标之前所在的世界坐标位置，所以我们要先求逆矩阵。
&nbsp;&nbsp;&nbsp;&nbsp;glm::mat4&nbsp;inverse_permat&nbsp;=&nbsp;glm::inverse(persmat);

&nbsp;&nbsp;&nbsp;&nbsp;glm::vec4&nbsp;near_world&nbsp;=&nbsp;inverse_permat&nbsp;*&nbsp;near_ndc;
&nbsp;&nbsp;&nbsp;&nbsp;glm::vec4&nbsp;far_world&nbsp;=&nbsp;inverse_permat&nbsp;*&nbsp;far_ndc;&nbsp;&nbsp;&nbsp;&nbsp;printf("before&nbsp;/w:&nbsp;\tnear_world:&lt;%9.3f,%9.3f,%9.3f,%9.3f&gt;\n\t\tfar_world:&nbsp;&lt;%9.3f,%9.3f,%9.3f,%9.3f&gt;\n",&nbsp;
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;near_world.x,&nbsp;near_world.y,&nbsp;near_world.z,&nbsp;near_world.w,&nbsp;
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;far_world.x,&nbsp;far_world.y,&nbsp;far_world.z,&nbsp;far_world.w);&nbsp;&nbsp;&nbsp;&nbsp;//&nbsp;我们知道&nbsp;OpenGL&nbsp;会自动进行透视除法（/w）来将透视矩阵转换后的坐标最终转换成&nbsp;NDC&nbsp;。
&nbsp;&nbsp;&nbsp;&nbsp;//&nbsp;而刚刚乘以逆矩阵只消除了透视投影，未消除透视除法，
&nbsp;&nbsp;&nbsp;&nbsp;//&nbsp;这时还应该再除以&nbsp;w&nbsp;分量，让&nbsp;w&nbsp;分量为&nbsp;1&nbsp;来消除透视除法的影响。
&nbsp;&nbsp;&nbsp;&nbsp;near_world&nbsp;/=&nbsp;near_world.w;
&nbsp;&nbsp;&nbsp;&nbsp;far_world&nbsp;/=&nbsp;far_world.w;&nbsp;&nbsp;&nbsp;&nbsp;printf("after&nbsp;/w:&nbsp;\tnear_world:&lt;%9.3f,%9.3f,%9.3f,%9.3f&gt;\n\t\tfar_world:&nbsp;&lt;%9.3f,%9.3f,%9.3f,%9.3f&gt;\n",
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;near_world.x,&nbsp;near_world.y,&nbsp;near_world.z,&nbsp;near_world.w,&nbsp;
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;far_world.x,&nbsp;far_world.y,&nbsp;far_world.z,&nbsp;far_world.w);&nbsp;&nbsp;&nbsp;&nbsp;printf("see,&nbsp;in&nbsp;right-hand&nbsp;the&nbsp;z&nbsp;axis&nbsp;of&nbsp;result&nbsp;match&nbsp;-neardistance&nbsp;and&nbsp;-fardistance\n");
}
输出结果如下：
before&nbsp;/w:&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;near_world:&lt;&nbsp;&nbsp;&nbsp;&nbsp;0.000,&nbsp;&nbsp;&nbsp;&nbsp;0.000,&nbsp;&nbsp;&nbsp;-1.000,&nbsp;&nbsp;&nbsp;&nbsp;1.000&gt;
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;far_world:&nbsp;&lt;&nbsp;&nbsp;&nbsp;&nbsp;0.000,&nbsp;&nbsp;&nbsp;&nbsp;0.000,&nbsp;&nbsp;&nbsp;-1.000,&nbsp;&nbsp;&nbsp;&nbsp;0.010&gt;
after&nbsp;/w:&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;near_world:&lt;&nbsp;&nbsp;&nbsp;&nbsp;0.000,&nbsp;&nbsp;&nbsp;&nbsp;0.000,&nbsp;&nbsp;&nbsp;-1.000,&nbsp;&nbsp;&nbsp;&nbsp;1.000&gt;
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;far_world:&nbsp;&lt;&nbsp;&nbsp;&nbsp;&nbsp;0.000,&nbsp;&nbsp;&nbsp;&nbsp;0.000,&nbsp;-100.000,&nbsp;&nbsp;&nbsp;&nbsp;1.000&gt;
see,&nbsp;in&nbsp;right-hand&nbsp;the&nbsp;z&nbsp;axis&nbsp;of&nbsp;result&nbsp;match&nbsp;-neardistance&nbsp;and&nbsp;-fardistance</pre>
    <p style="box-sizing: inherit; -webkit-tap-highlight-color: transparent; margin-bottom: 16px; color: rgb(61, 70, 77); font-family: &quot;Pingfang SC&quot;, STHeiti, &quot;Lantinghei SC&quot;, &quot;Open Sans&quot;, Arial, &quot;Hiragino Sans GB&quot;, &quot;Microsoft YaHei&quot;, &quot;WenQuanYi Micro Hei&quot;, SimSun, sans-serif; white-space: normal; background-color: rgb(248, 248, 248);">查看输出结果验证了上面示例的正确性。有一点注意是通过逆矩阵转换后，还是需要手动除以 w 分量，使得 w 分量为 1 才是我们想要的结果，因为<strong style="box-sizing: inherit; -webkit-tap-highlight-color: transparent;">透视除法（perspective division）</strong>是 OpengGL 自动执行的，不包括在透视投影矩阵中。</p>
    <h1 style="box-sizing: inherit; -webkit-tap-highlight-color: transparent; font-size: 24px; font-weight: 500; color: rgb(61, 70, 77); font-family: &quot;Pingfang SC&quot;, STHeiti, &quot;Lantinghei SC&quot;, &quot;Open Sans&quot;, Arial, &quot;Hiragino Sans GB&quot;, &quot;Microsoft YaHei&quot;, &quot;WenQuanYi Micro Hei&quot;, SimSun, sans-serif; white-space: normal; background-color: rgb(248, 248, 248);">在 cpp 代码中实现 MVP 坐标变换以及透视除法</h1>
    <p style="box-sizing: inherit; -webkit-tap-highlight-color: transparent; margin-bottom: 16px; color: rgb(61, 70, 77); font-family: &quot;Pingfang SC&quot;, STHeiti, &quot;Lantinghei SC&quot;, &quot;Open Sans&quot;, Arial, &quot;Hiragino Sans GB&quot;, &quot;Microsoft YaHei&quot;, &quot;WenQuanYi Micro Hei&quot;, SimSun, sans-serif; white-space: normal; background-color: rgb(248, 248, 248);">通常在学习 OpenGL 时，示例代码都是在 vertex shader 中用矩阵乘以顶点属性坐标后，然后赋值给 gl_Position 。通过直接的方式是看不了具体的坐标值。但是我们可以把这一过程在 cpp 代码中实现，并打印出来假设理解。这样赋值给 gl_Position 的坐标就是最终的 NDC 坐标，因为赋值给 gl_Position 后，OpenGL 虽然会再继续执行透视除法，但此时值是不变的。经过分析后发现是可行的，那就来写代码吧。</p>
    <ul style="box-sizing: inherit; -webkit-tap-highlight-color: transparent; margin-left: 30px; padding: 8px 15px; color: rgb(61, 70, 77); font-family: &quot;Pingfang SC&quot;, STHeiti, &quot;Lantinghei SC&quot;, &quot;Open Sans&quot;, Arial, &quot;Hiragino Sans GB&quot;, &quot;Microsoft YaHei&quot;, &quot;WenQuanYi Micro Hei&quot;, SimSun, sans-serif; white-space: normal; background-color: rgb(248, 248, 248);" class=" list-paddingleft-2">
     <li><p>绘制 6 个三角形，左边 3 个为一组，采用右手坐标系绘制，右边 3 个为一组，采用左手坐标系绘制。并且左边三角形坐标与右边相应的三角形坐标仅仅是 x 轴坐标不同。</p></li>
     <li><p>观察左边一组，比较它们的 z 坐标并查看最终的三角形显示的前后顺序。</p></li>
     <li><p>观察右边一组，比较它们的 z 坐标并查看最终的三角形显示的前后顺序。</p></li>
     <li><p>比较左边与右边 y 和 z 坐标相同的三角形，它们的显示顺序是相反的。</p></li>
     <li><p>查看日志，观察最终的 NDC 坐标都是基于左手坐标系。</p></li>
    </ul>
    <p style="box-sizing: inherit; -webkit-tap-highlight-color: transparent; margin-bottom: 16px; color: rgb(61, 70, 77); font-family: &quot;Pingfang SC&quot;, STHeiti, &quot;Lantinghei SC&quot;, &quot;Open Sans&quot;, Arial, &quot;Hiragino Sans GB&quot;, &quot;Microsoft YaHei&quot;, &quot;WenQuanYi Micro Hei&quot;, SimSun, sans-serif; white-space: normal; background-color: rgb(248, 248, 248);">由于我们在 cpp 中完成了很多功能，shader 就很简单。</p>
    <pre class="hljs actionscript" style="box-sizing: inherit; -webkit-tap-highlight-color: transparent; overflow-x: auto; padding: 2px; background-color: rgb(63, 63, 63); color: rgb(220, 220, 220); border-radius: 3px; line-height: 1.4; word-wrap: normal; font-size: 13px; font-family: Menlo, Monaco, Consolas, &quot;Courier New&quot;, monospace;">const&nbsp;GLchar&nbsp;*vertexcode&nbsp;=&nbsp;
"#version&nbsp;330&nbsp;core&nbsp;\n""layout(location&nbsp;=&nbsp;0)&nbsp;in&nbsp;vec4&nbsp;pos_modelspace;&nbsp;\n""out&nbsp;vec4&nbsp;o_color;&nbsp;\n""void&nbsp;main()&nbsp;{&nbsp;\n""&nbsp;&nbsp;&nbsp;&nbsp;gl_Position&nbsp;=&nbsp;pos_modelspace;&nbsp;\n""}";const&nbsp;GLchar&nbsp;*fragmentcode&nbsp;=&nbsp;
"#version&nbsp;330&nbsp;core&nbsp;\n""uniform&nbsp;vec3&nbsp;bg;&nbsp;\n""out&nbsp;vec3&nbsp;color;&nbsp;\n""in&nbsp;vec4&nbsp;o_color;&nbsp;\n""void&nbsp;main()&nbsp;{&nbsp;\n""&nbsp;&nbsp;&nbsp;&nbsp;color&nbsp;=&nbsp;bg;&nbsp;\n""}";</pre>
    <p style="box-sizing: inherit; -webkit-tap-highlight-color: transparent; margin-bottom: 16px; color: rgb(61, 70, 77); font-family: &quot;Pingfang SC&quot;, STHeiti, &quot;Lantinghei SC&quot;, &quot;Open Sans&quot;, Arial, &quot;Hiragino Sans GB&quot;, &quot;Microsoft YaHei&quot;, &quot;WenQuanYi Micro Hei&quot;, SimSun, sans-serif; white-space: normal; background-color: rgb(248, 248, 248);">由于不需要采用 model matrix ，所以这里只进行 MVP 中的 VP 转换，代码如下。基本原理就是调用&nbsp;<code style="box-sizing: inherit; -webkit-tap-highlight-color: transparent; border-radius: 3px; padding: 2px; font-size: 13px;">glMapBufferRange</code>&nbsp;把 buffer 的地址映射出来，然后依次修改 buffer 来对顶点进行坐标变换。</p>
    <pre class="hljs perl" style="box-sizing: inherit; -webkit-tap-highlight-color: transparent; overflow-x: auto; padding: 2px; background-color: rgb(63, 63, 63); color: rgb(220, 220, 220); border-radius: 3px; line-height: 1.4; word-wrap: normal; font-size: 13px; font-family: Menlo, Monaco, Consolas, &quot;Courier New&quot;, monospace;">//&nbsp;位置属性包含&nbsp;4&nbsp;个元素&nbsp;(x,&nbsp;y,&nbsp;z,&nbsp;w)&nbsp;，直接在这里进行矩阵计算。
void
initdraw(int&nbsp;len,&nbsp;GLfloat&nbsp;**multi_vertices,&nbsp;GLuint&nbsp;*boarr,&nbsp;int&nbsp;trianglebytes,&nbsp;const&nbsp;glm::mat4&nbsp;&amp;view,&nbsp;const&nbsp;glm::mat4&nbsp;&amp;perspective)&nbsp;{
&nbsp;&nbsp;&nbsp;&nbsp;GLfloat&nbsp;*ptr,&nbsp;*startptr;&nbsp;&nbsp;&nbsp;&nbsp;int&nbsp;attrnum&nbsp;=&nbsp;trianglebytes&nbsp;/&nbsp;(sizeof(GLfloat)&nbsp;*&nbsp;VERTEX_POSATTR_NUM);&nbsp;&nbsp;&nbsp;&nbsp;printf("init&nbsp;draw&nbsp;now,&nbsp;[triangle&nbsp;num:%d]&nbsp;[vertex&nbsp;num&nbsp;per&nbsp;triangle:%d]\n",&nbsp;len,&nbsp;attrnum);&nbsp;&nbsp;&nbsp;&nbsp;for&nbsp;(int&nbsp;i&nbsp;=&nbsp;0;&nbsp;i&nbsp;&lt;&nbsp;len;&nbsp;i++)&nbsp;{&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;printf("init&nbsp;draw&nbsp;with&nbsp;triangle:%d\n",&nbsp;i);
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;GLfloat&nbsp;*triangle&nbsp;=&nbsp;multi_vertices[i];
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;glBindBuffer(GL_ARRAY_BUFFER,&nbsp;boarr[i]);
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;glBufferData(GL_ARRAY_BUFFER,&nbsp;trianglebytes,&nbsp;NULL,&nbsp;GL_STATIC_DRAW);

&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;startptr&nbsp;=&nbsp;ptr&nbsp;=&nbsp;(GLfloat*)glMapBufferRange(GL_ARRAY_BUFFER,&nbsp;0,&nbsp;trianglebytes,&nbsp;GL_MAP_WRITE_BIT&nbsp;|&nbsp;GL_MAP_INVALIDATE_BUFFER_BIT);&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;for&nbsp;(int&nbsp;j&nbsp;=&nbsp;0;&nbsp;j&nbsp;&lt;&nbsp;attrnum;&nbsp;j++)&nbsp;{
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;GLfloat&nbsp;*vertex&nbsp;=&nbsp;triangle&nbsp;+&nbsp;j&nbsp;*&nbsp;VERTEX_POSATTR_NUM;
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;ptr&nbsp;=&nbsp;startptr&nbsp;+&nbsp;j&nbsp;*&nbsp;VERTEX_POSATTR_NUM;

&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;glm::vec4&nbsp;point(vertex[0],&nbsp;vertex[1],&nbsp;vertex[2],&nbsp;vertex[3]);&nbsp;//&nbsp;位置属性包含的元素&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;printf("before&nbsp;point:%d&nbsp;%10.3f,%10.3f,%10.3f,%10.3f\n",&nbsp;j,&nbsp;vertex[0],&nbsp;vertex[1],&nbsp;vertex[2],&nbsp;vertex[3]);
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;point&nbsp;=&nbsp;perspective&nbsp;*&nbsp;view&nbsp;*&nbsp;point;&nbsp;//&nbsp;坐标转换&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;printf("after&nbsp;point&nbsp;:%d&nbsp;%10.3f,%10.3f,%10.3f,%10.3f\n",&nbsp;j,&nbsp;point.x,&nbsp;point.y,&nbsp;point.z,&nbsp;point.w);&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;printf("after&nbsp;/w&nbsp;&nbsp;&nbsp;&nbsp;:%d&nbsp;%10.3f,%10.3f,%10.3f\n",&nbsp;j,&nbsp;point.x/point.w,&nbsp;point.y/point.w,&nbsp;point.z/point.w);
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;memcpy(ptr,&nbsp;glm::value_ptr(point),&nbsp;sizeof(point));
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;}&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;if&nbsp;(glUnmapBuffer(GL_ARRAY_BUFFER)&nbsp;==&nbsp;GL_FALSE)&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;printf("fail&nbsp;to&nbsp;unmap&nbsp;buffer\n");
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;glBindBuffer(GL_ARRAY_BUFFER,&nbsp;0);
&nbsp;&nbsp;&nbsp;&nbsp;}
&nbsp;&nbsp;&nbsp;&nbsp;fflush(stdout);
}

void&nbsp;
startdraw(int&nbsp;len,&nbsp;GLuint&nbsp;*boarr,&nbsp;GLuint&nbsp;bglocation,&nbsp;GLfloat&nbsp;**bgarr)&nbsp;{&nbsp;&nbsp;&nbsp;&nbsp;for&nbsp;(int&nbsp;i&nbsp;=&nbsp;0;&nbsp;i&nbsp;&lt;&nbsp;len;&nbsp;i++)&nbsp;{
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;glUniform3fv(bglocation,&nbsp;1,&nbsp;bgarr[i]);

&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;glBindBuffer(GL_ARRAY_BUFFER,&nbsp;boarr[i]);
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;glVertexAttribPointer(0,&nbsp;VERTEX_POSATTR_NUM,&nbsp;GL_FLOAT,&nbsp;GL_FALSE,&nbsp;0,&nbsp;0);
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;glDrawArrays(GL_TRIANGLES,&nbsp;0,&nbsp;3);&nbsp;&nbsp;&nbsp;&nbsp;
&nbsp;&nbsp;&nbsp;&nbsp;}
}</pre>
    <p style="box-sizing: inherit; -webkit-tap-highlight-color: transparent; margin-bottom: 16px; color: rgb(61, 70, 77); font-family: &quot;Pingfang SC&quot;, STHeiti, &quot;Lantinghei SC&quot;, &quot;Open Sans&quot;, Arial, &quot;Hiragino Sans GB&quot;, &quot;Microsoft YaHei&quot;, &quot;WenQuanYi Micro Hei&quot;, SimSun, sans-serif; white-space: normal; background-color: rgb(248, 248, 248);">运行截图如下。</p>
    <p style="box-sizing: inherit; -webkit-tap-highlight-color: transparent; margin-bottom: 16px; color: rgb(61, 70, 77); font-family: &quot;Pingfang SC&quot;, STHeiti, &quot;Lantinghei SC&quot;, &quot;Open Sans&quot;, Arial, &quot;Hiragino Sans GB&quot;, &quot;Microsoft YaHei&quot;, &quot;WenQuanYi Micro Hei&quot;, SimSun, sans-serif; white-space: normal; background-color: rgb(248, 248, 248);"><img src="https://static.oschina.net/uploads/img/201701/12231818_hS8B.png" alt="run" title="run"></p>
    <p style="box-sizing: inherit; -webkit-tap-highlight-color: transparent; margin-bottom: 16px; color: rgb(61, 70, 77); font-family: &quot;Pingfang SC&quot;, STHeiti, &quot;Lantinghei SC&quot;, &quot;Open Sans&quot;, Arial, &quot;Hiragino Sans GB&quot;, &quot;Microsoft YaHei&quot;, &quot;WenQuanYi Micro Hei&quot;, SimSun, sans-serif; white-space: normal; background-color: rgb(248, 248, 248);">程序输出如下所示。</p>
    <p style="box-sizing: inherit; -webkit-tap-highlight-color: transparent; margin-bottom: 16px; color: rgb(61, 70, 77); font-family: &quot;Pingfang SC&quot;, STHeiti, &quot;Lantinghei SC&quot;, &quot;Open Sans&quot;, Arial, &quot;Hiragino Sans GB&quot;, &quot;Microsoft YaHei&quot;, &quot;WenQuanYi Micro Hei&quot;, SimSun, sans-serif; white-space: normal; background-color: rgb(248, 248, 248);"><img src="https://static.oschina.net/uploads/img/201701/12232304_vMAj.png" alt="output" title="output"></p>
    <ul style="box-sizing: inherit; -webkit-tap-highlight-color: transparent; margin-left: 30px; padding: 8px 15px; color: rgb(61, 70, 77); font-family: &quot;Pingfang SC&quot;, STHeiti, &quot;Lantinghei SC&quot;, &quot;Open Sans&quot;, Arial, &quot;Hiragino Sans GB&quot;, &quot;Microsoft YaHei&quot;, &quot;WenQuanYi Micro Hei&quot;, SimSun, sans-serif; white-space: normal; background-color: rgb(248, 248, 248);" class=" list-paddingleft-2">
     <li><p>观察运行结果发现，两组相同的坐标（仅仅是 x 坐标不同）因为采用了右手和左手坐标系，左边的三角形显示顺序是与右边相反的。</p></li>
     <li><p>观察程序输出结果，发现采用左手还是右手坐标系，最终的 NDC 坐标都是基于左手坐标系，两组中显示最前的三角形 z 值都是最小的。</p></li>
    </ul>
    <h1 style="box-sizing: inherit; -webkit-tap-highlight-color: transparent; font-size: 24px; font-weight: 500; color: rgb(61, 70, 77); font-family: &quot;Pingfang SC&quot;, STHeiti, &quot;Lantinghei SC&quot;, &quot;Open Sans&quot;, Arial, &quot;Hiragino Sans GB&quot;, &quot;Microsoft YaHei&quot;, &quot;WenQuanYi Micro Hei&quot;, SimSun, sans-serif; white-space: normal; background-color: rgb(248, 248, 248);">有趣的问题</h1>
    <p style="box-sizing: inherit; -webkit-tap-highlight-color: transparent; margin-bottom: 16px; color: rgb(61, 70, 77); font-family: &quot;Pingfang SC&quot;, STHeiti, &quot;Lantinghei SC&quot;, &quot;Open Sans&quot;, Arial, &quot;Hiragino Sans GB&quot;, &quot;Microsoft YaHei&quot;, &quot;WenQuanYi Micro Hei&quot;, SimSun, sans-serif; white-space: normal; background-color: rgb(248, 248, 248);">查看运行截图发现左右两边最靠前的三角形，它们似乎在一条水平线上。查看程序输出确实是这样，它们最终的 NDC 的 z 轴坐标是一样的，都是 0.952 。到代码仓库&nbsp;<a href="http://git.oschina.net/iirecord/blogsnippet" style="box-sizing: inherit; -webkit-tap-highlight-color: transparent; border: none; outline: 0px; transition: color 0.3s; text-decoration: none; color: rgb(68, 102, 187);">blogsnippet</a>/opengl/lefthand-or-righthand 目录下查看上例完整的代码，找到左边的 view matrix 代码和右边的 view matrix 代码（这里简单的 view matrix 就没有通过调用 lookAt 产生了）。</p>
    <pre class="hljs go" style="box-sizing: inherit; -webkit-tap-highlight-color: transparent; overflow-x: auto; padding: 2px; background-color: rgb(63, 63, 63); color: rgb(220, 220, 220); border-radius: 3px; line-height: 1.4; word-wrap: normal; font-size: 13px; font-family: Menlo, Monaco, Consolas, &quot;Courier New&quot;, monospace;">viewleft&nbsp;=&nbsp;glm::translate(viewleft,&nbsp;glm::vec3(0.0f,&nbsp;0.0f,&nbsp;-10.0f));
viewright&nbsp;=&nbsp;glm::translate(viewright,&nbsp;glm::vec3(0.0f,&nbsp;0.0f,&nbsp;4.0f));</pre>
    <p style="box-sizing: inherit; -webkit-tap-highlight-color: transparent; margin-bottom: 16px; color: rgb(61, 70, 77); font-family: &quot;Pingfang SC&quot;, STHeiti, &quot;Lantinghei SC&quot;, &quot;Open Sans&quot;, Arial, &quot;Hiragino Sans GB&quot;, &quot;Microsoft YaHei&quot;, &quot;WenQuanYi Micro Hei&quot;, SimSun, sans-serif; white-space: normal; background-color: rgb(248, 248, 248);">结合三角形的坐标，如果能明白产生 viewleft 时，z 轴偏移是 -10.0f，产生 viewright 时，z 轴偏移是 4.0f ，通过这样设置就可以产生上图的效果，那么表示你就理解了其中的坐标变换。</p>
    <p><br></p>
   </div> 
  </div>  
 </body>
</html>